/**
* CircleDequeueBase.h
*
* Great Wisdom Confidential
* Copyright 2013 Great Wisdom Corporation. All Rights Reserved.
*
* Author: Lixin Guo
* Date: 6/6/2013
* Description: 
*/

#include <stdlib.h>
#include <vector>
#include "GlobalDefinition.h"
#include "ObserverBase.h"
#include "DataDefinition.h"
#include "DateTime.h"

#pragma once
using namespace std;

extern LPCompress g_funCompress;
extern LPUncompress g_funUncompress;

/**  
* Error code definition
*/
const int CD_DEFAULT = 100;					// Default error code
const int CD_WARNING_LEN_DISMATCH = 101;	// Head of dbf file dismatch
const int CD_WARNING_CONTENT_DISMATCH = 102;// Content of dbf file dismatch
const int CD_ERROR_LENGTH_OVERFLOW = 103;	// Length greater than expected

const int COMPRESSED_OK = 0;
const int COMPRESSED_MEM_ERROR = -4;
const int COMPRESSED_BUF_ERROR = -5;

/**  
* Struct to define one element in the circle queue.
*/
typedef struct tagDequeueElement
{
	unsigned int m_uiSize;			// Original size of the buffer
	unsigned int m_uiAllocedSize;	// Allocated size of the buffer
	int m_iIndex;					// Index of current element
	int m_iEntireIndex;				// Associated with parent element index, assign -1 if no parent element exist.
	char* m_szElementAddr;			// Address of this element
	tagDequeueElement* m_pNext;		// Next element address
	tagDequeueElement* m_pPrevious; // Previous element address
	
	/**
	* A constructor.
	*/
	tagDequeueElement()
		:m_uiSize(0)
		,m_uiAllocedSize(0)
		,m_iIndex(-1)
		,m_iEntireIndex(-1)
		,m_szElementAddr(NULL)
		,m_pNext(NULL)
		,m_pPrevious(NULL)
	{
	}

	/**
	* A destructor.
	*/
	~tagDequeueElement()
	{
		if(NULL != m_szElementAddr)
		{
			free(m_szElementAddr);
			m_szElementAddr = NULL;
		}
	}
}DequeueElement;

/**  
*  Struct to define the first element in the queue
*/
typedef struct tagDequeueHeader
{
	DequeueElement* m_pHeader;			// First element in the queue

	/**
	* A constructor.
	*/
	tagDequeueHeader()
		:m_pHeader(NULL)
	{
	}
}DequeueHeader;

/**  
*  Circle dequeue class. 
*/
class CircleDequeueBase
{
public:
	/**
	* A constructor.
	* @param p_uiElementAmount Total number of elements int the queue.
	* @param p_uiElementSize Size for one element.
	* @param p_uiBeginSequence Begin sequence for this queue
	* @param p_uiEndSequence End sequence for this queue
	*/
	CircleDequeueBase(unsigned int p_uiEntireBufferSize,unsigned int p_uiElementAmount, unsigned int p_uiElementSize,unsigned int p_uiBeginSequence,unsigned int p_uiEndSequence,unsigned char p_uiEntireDataType, unsigned char p_uiIncrementDataType);

	/**
	* A destructor.
	*/
	virtual ~CircleDequeueBase();

	/**        
	* Initialize the circle queue. Construct and connect circle dequeue with allocated element.
	* @return Indicates whether success or not
	*/
	bool Initialize();

	/**
	* Post data to client socket.This function will be called when a client connect to IOCP server firstly
	* @param p_iSocket Client socket
	* @return Indicates if post data is successful
	*/
	bool PostData(SOCKET p_iSocket);

	/**
	* Write element in the circle queue and send out the data in the element
	* @param p_uiBufferSize Size of the data need to be write
	* @param p_szBuffer Data buffer address, which should be copied into element.
	* @return Indicate whether write data is fail or successful
	*/
	virtual bool WriteData(char* p_szBuffer,unsigned int p_uiBufferSize);

	/**
	* [OLYN FOR RELAY] Write element in the circle queue and send out the data in the element
	* @param p_szBufferAddress Data buffer address, [FORMAT] CompressedHead + CompressedContent
	* @param p_uiBufferSize Size of the data need to be write
	* @param p_cType File type
	* @param p_bIsResend Indicates whether data should be resend
	* @return Indicate whether write data is fail or successful
	*/
	virtual bool WriteData(char* p_szBufferAddress, unsigned int p_uiBufferSize,  unsigned char p_cType, bool p_bIsResend);

	/**
	* Attach observer to this circle queue. Observers will be notified when element is ready to send out.
	* @param ObserverBase Base class of observer class
	*/
	void Attach(ObserverBase* p_pcObserverBase);

	/**
	* Detach observer from circle queue.
	* @param ObserverBase Base class of observer class
	*/
	void Detach(ObserverBase* p_pcObserverBase);

	///**
	//* Add new client socket to the circle queue.
	//* @param Base class of observer class
	//*/
	//void AddSocket(SOCKET p_iClient);

	/**        
	* Remove client socket from circle queue.
	* @param p_stSocket Client socket
	*/
	void DeleteSocket(SOCKET p_stSocket);

	/**        
	* Clear all the socket in the circle queue with exceptions
	* @param p_vecExceptSockets Some except sockets that should not be cleared
	*/
	void ClearSocket(vector<SOCKET> p_vecExceptSockets);

	/**
	* Reset dequeue when specific occasions meets.
	* @return Indicates 
	*/
	bool ResetDequeue();

	/**
	* Get Last error code.
	* @return Error code
	*/
	int GetLastError();

	/**
	* Print circle queue to std::out. This function could be used for test or debug.
	* @param p_iSequence 
	*		 0:Positive sequence 
	*		 1:Negative sequence
	*/
	void PrintDequeue(int p_iSequence);

	/**
	* Get address and length of send buffer.
	* @param p_szBufferAddress Send buffer address 
	* @return Send buffer length
	*/
	unsigned int GetSendBuffer(char** p_szBufferAddress);

	/**
	* Get address and length of entire buffer.
	* @param p_szBufferAddress Send buffer address 
	* @return Send buffer length
	*/
	unsigned int GetEntireBuffer(char** p_szBufferAddress);

	/**
	* Set flag to indicate circle queue to send out entire buffer rather than increment buffer
	*/
	void SetEntireFlag();

	/**
	* Move entire buffer data to send buffer
	*/
	void MoveToSendBuffer(unsigned char p_ucFileType);
protected:
	/**
	* Send out some specified elements according to base index. All the elements MUST before current element.
	* @return Indicates if sending is successful
	*/
	bool SendPreviousPackets(vector<SOCKET> p_vecSocketList,DequeueElement* p_stElement);

	/**
	* Delete an specific element.
	* @param p_pstElement The element to be removed
	* @return Next element
	*/
	DequeueElement* DeleteElement(DequeueElement* p_pstElement);

	/**
	* Add an element behind current element
	* @param p_pstElement Current element
	* @param p_uiSize New element size
	* @return New element allocated
	*/
	DequeueElement* AddElementBehind(DequeueElement* p_pstElement,unsigned int p_uiSize);

	/**
	* Add an element before current element
	* @param p_pstElement Current element
	* @param p_uiSize New element size
	* @return New element allocated
	*/
	DequeueElement* AddElementBefore(DequeueElement* pElement,unsigned int p_uiElementSize);

	/**
	* Notify observers to send out datas
	* @param p_vecSocketList Client socket list
	* @param p_szBuffer Buffer address
	* @param p_uiBufferSize Buffer size
	* @return Indicates if notify client is successful
	*/
	bool Notify(vector<SOCKET>& p_vecSocketList, char* p_szBuffer, unsigned int p_uiBufferSize);

	/**
	* Get the first address of element associated with specific base index
	* @param p_iEntireIndex Entire packet index
	* @return First element associated with base index 
	*/
	DequeueElement* GetPreviousAddress(int p_iEntireIndex);

	/**
	* Update socket list index
	* @param p_iSocket Client socket
	* @param p_iIndex Base index
	*/
	void UpdateListIndex(SOCKET p_iSocket,int p_iIndex);

	/**
	* Write data as element into the queue
	* @param p_szBufferAddress Buffer address
	* @param p_uiBufferSize Buffer size
	*/
	void WriteElement(char* p_szBufferAddress, unsigned int p_uiBufferSize);

	bool CompressData(char* p_szDestBuffer, unsigned int& p_ulDestSize, char* p_szSrcBuffer, unsigned int p_ulSrcSize,unsigned char p_uiDataType);
	bool UncompressData(char* p_szDestBuffer, unsigned int& p_ulDestSize, char* p_szSrcBuffer, unsigned int p_ulSrcSize);
	int CompareData(char* p_szSrcBuffer, unsigned int p_uiSrcSize);
	bool CombineData(char* p_szSrcBuffer, unsigned int p_uiSrcSize);
	void RemoveClient(vector<SOCKET>& p_vecSourceList, vector<SOCKET>& p_vecDeleteList);
	void RemoveClientWithException(vector<SOCKET>& p_vecSourceList, vector<SOCKET>& p_vecExceptionList);
	bool IsSocketExist(SOCKET p_iSocket);


protected:
    /**
	* Amount of element
	*/
	unsigned int m_uiElementAmount;

    /**
	* Size of intialized element. New allocated element excluded.
	*/
	unsigned int m_uiElementSize;
	
	/**
	* Header of circle dequeue
	*/
	DequeueHeader m_stDequeueHeader;

    /**
	* Current element
	*/
	DequeueElement* m_pstCurrentAddr;

    /**
	* Indicates if need reset
	*/
	bool  m_bIsReset;
	
    /**
	* Worker thread mutex
	*/
	CRITICAL_SECTION   m_csLock;

    /**
	* Compressed entire data buffer
	*/
	char* m_pszSendBuffer;

	/**
	* Compressed entire data length
	*/
	unsigned int m_uiSendBufferLength;

	/**
	* Compressed entire data size
	*/
	unsigned int m_uiSendBufferSize;

	/**
	* Compressed entire data index
	*/
	unsigned int m_uiSendBufferIndex;

	/**
	* Uncompressed entire data buffer
	*/
	char* m_pszEntireBuffer;

    /**
	* Entire buffer size
	*/
	unsigned int m_uiEntireBufferSize;

	/**
	* Uncompressed entire data length
	*/
	unsigned int m_uiEntireBufferLength;

	/**
	* Uncompressed data cache
	*/
	char* m_pszCache;

	/**
	* Uncompressed data cache size
	*/
	unsigned int m_uiCacheSize;

	/**
	* Uncompressed data cache length
	*/
	unsigned int m_uiCacheLength;

	/**
	* Compressed increment data buffer
	*/
	char* m_szCompressCache;

	/**
	* Compressed increment data size
	*/
	unsigned int m_uiComCacheSize;

	/**
	* Compressed data cache length
	*/
	unsigned int m_uiComCacheLength;

	/**
	* Client sockets connect to server firstly
	*/
	vector<SOCKET> m_vecRookieSocketList;

	/**
	* alived client sockets 
	*/
	vector<SOCKET> m_vecAlivedSocketList;

	/**
	* Begin sequence of entire data
	*/
	unsigned int m_uiBeginSequence;

	/**
	* End sequence of entire data
	*/
	unsigned int m_uiEndSequence;

	/**
	* Current sequence of entire data
	*/
	unsigned int m_uiCurrentSequence;

	/**
	* Error ID
	*/
	int m_iLastError;

	/**
	* Observer container to store observers which attach to the circle queue.
	*/	
	vector<ObserverBase*> m_vecObserverList;
	
	/**
	* Entire buffer data type
	*/	
	unsigned char m_ucEntireDataType;

	/**
	* Increment data type
	*/	
	unsigned char m_ucIncrementDataType;

	/**
	* Write count
	*/	
	unsigned int m_uiCount;

	/**
	* Last updated time
	*/
	DateTime m_clsLastDate;
};